package com.yb.guigu.pick.activity.service.impl;

import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Service
public class SplitRedPacketsServiceImpl {
    //红包最大金额
    private static final BigDecimal MAXMONEY = new BigDecimal("200");

    /**
     * 红包拆分生成list集合
     * 1.生成count个红包的list，将最小金额分配到每个红包上
     * 2.随机生成一个数值，在原list上做加法
     *
     * @param money    总金额
     * @param count    总数
     * @param minmoney 最小金额
     * @param maxmoney 最大金额
     * @param bigred   大包固定金额
     * @param bigcount 大包个数
     * @return
     */
    public List<BigDecimal> splitRedPackets(BigDecimal money, BigDecimal maxmoney, BigDecimal minmoney, BigDecimal count, BigDecimal bigred, BigDecimal bigcount) {
        //计算小包金额总数和总金额
        count = count.subtract(bigcount);
        money = money.subtract(bigcount.multiply(bigred));

        //大包固定金额集合
        List<BigDecimal> bigList = new ArrayList<>();
        for (int i = 0; i < bigcount.intValue(); i++) {
            bigList.add(bigred);
        }

        //原始list--小包list
        List<BigDecimal> list = new ArrayList<>();
        maxmoney = (maxmoney.compareTo(MAXMONEY) > 0) ? MAXMONEY : maxmoney;

        /**
         * 1.将最小金额分配到每个红包上
         * 2.减去分配的小包金额
         * 3.剩余总金额 =总金额-最小金额*最小金额数
         */
        for (int i = 0; i < count.intValue(); i++) {
            list.add(minmoney);
        }
        BigDecimal minsum = minmoney.multiply(count);
        BigDecimal totalMoney = money.subtract(minsum);
        BigDecimal realMaxmoney = maxmoney.subtract(minmoney);

        //判断是否符合取值区间
        if (!isRight(totalMoney, count, realMaxmoney, new BigDecimal("0"))) {
            return null;
        }

        //合并后的新包
        List<BigDecimal> listnew = new ArrayList<BigDecimal>();
        for (int i = 0; i < list.size(); i++) {
            BigDecimal one = randomRedPacket(totalMoney, new BigDecimal("0"), realMaxmoney, new BigDecimal(count.intValue() - i));
            listnew.add(list.get(i).add(one));
            totalMoney = totalMoney.subtract(one);
        }

        //合并打包固定金额集合
        listnew.addAll(bigList);
        Collections.shuffle(listnew);
        return listnew;
    }


    /**
     * 随机方法产生一个在最大值和最小值之间的一个红包，
     * 并判断该红包是否合法，是否在产生这个红包之后红包金额变成负数。
     * 另外，在这次产生红包值较小时，下一次就产生一个大一点的红包。
     *
     * @param money 总金额
     * @param mins  最小金额
     * @param maxs  最大金额
     * @param count 红包总数
     * @return
     */
    private BigDecimal randomRedPacket(BigDecimal money, BigDecimal mins, BigDecimal maxs, BigDecimal count) {
        if (count.intValue() == 1) {
            return money.setScale(2, RoundingMode.UP);
        }
        if (mins.compareTo(maxs) == 0) {
            return mins;//如果最大值和最小值一样，就返回mins
        }
        BigDecimal max = (maxs.compareTo(money) > 0) ? money : maxs;
        //返回指定范围的随机数,保留两位小数
        BigDecimal random = BigDecimal.valueOf(Math.random());
        BigDecimal middle = maxs.subtract(mins);
        BigDecimal middle2 = random.multiply(middle).setScale(2, RoundingMode.HALF_UP);
        BigDecimal one = middle2.add(mins);

        BigDecimal moneyOther = money.subtract(one);
        if (isRight(moneyOther, count.subtract(new BigDecimal("1")), maxs, mins)) {
            return one;
        } else {
            //重新分配
            BigDecimal avg = moneyOther.divide(count.subtract(new BigDecimal("1")), 2, RoundingMode.UP);
            if (avg.compareTo(mins) < 0) {
                return randomRedPacket(money, mins, one, count);
            } else if (avg.compareTo(maxs) > 0) {
                return randomRedPacket(money, one, maxs, count);
            }
        }
        return one;
    }

    /**
     * 判断是否符合取值区间
     *
     * @param money 总金额
     * @param count 总数
     * @return
     */
    private boolean isRight(BigDecimal money, BigDecimal count, BigDecimal maxs, BigDecimal mins) {
        BigDecimal avg = money.divide(count, 2, RoundingMode.UP);
        if (avg.compareTo(mins) < 0) {
            return false;
        } else return avg.compareTo(maxs) <= 0;
    }
}